#!/bin/bash -p
# see: https://developer.apple.com/library/archive/documentation/OpenSource/Conceptual/ShellScripting/ShellScriptSecurity/ShellScriptSecurity.html#//apple_ref/doc/uid/TP40004268-CH8-SW29

# EMBA - EMBEDDED LINUX ANALYZER
#
# Copyright 2020-2023 Siemens AG
# Copyright 2020-2025 Siemens Energy AG
#
# EMBA comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
# welcome to redistribute it under the terms of the GNU General Public License.
# See LICENSE file for usage of this software.
#
# EMBA is licensed under GPLv3
# SPDX-License-Identifier: GPL-3.0-only
#
# Author(s): Michael Messner, Pascal Eckmann
# Contributor(s): Stefan Haboeck

# Description:  Main script for load all necessary files and call main function of modules

INVOCATION_PATH="."

import_helper() {
  local lHELPERS_ARR=()
  local lHELPER_CNT=0
  local lHELPER_FILE=""
  mapfile -d '' lHELPERS_ARR < <(find "${HELP_DIR}" -iname "helpers_emba_*.sh" -print0 2> /dev/null)
  for lHELPER_FILE in "${lHELPERS_ARR[@]}" ; do
    if [[ $(head -n 1 "${lHELPER_FILE}") =~ ^#!.*bash ]] && ! [[ "${lHELPER_FILE}" =~ \ |\' ]] ; then
      # https://github.com/koalaman/shellcheck/wiki/SC1090
      # shellcheck source=/dev/null
      source "${lHELPER_FILE}"
      (( lHELPER_CNT+=1 ))
    fi
  done
  print_output "==> ""${GREEN}""Imported ""${lHELPER_CNT}"" helper files""${NC}" "no_log"
}

import_module() {
  local lMODULES_ARR=()
  local lMODULES_LOCAL_ARR=()
  local lMODULES_EMBA_ARR=()
  local lMODULE_COUNT=0
  local lMODULE_FILE=""
  # to ensure we are only auto load modules from the modules main directory we set maxdepth
  # with this in place we can create sub directories per module. For using/loading stuff from
  # these sub directories the modules are responsible!
  mapfile -t lMODULES_EMBA_ARR < <(find "${MOD_DIR}" -maxdepth 1 -name "*.sh" | sort -V 2> /dev/null)
  if [[ -d "${MOD_DIR_LOCAL}" ]]; then
    mapfile -t lMODULES_LOCAL_ARR < <(find "${MOD_DIR_LOCAL}" -maxdepth 1 -name "*.sh" 2>/dev/null | sort -V 2> /dev/null)
  fi
  lMODULES_ARR=( "${lMODULES_EMBA_ARR[@]}" "${lMODULES_LOCAL_ARR[@]}" )
  for lMODULE_FILE in "${lMODULES_ARR[@]}" ; do
    if ( file -b "${lMODULE_FILE}" | grep -q "shell script" ) && ! [[ "${lMODULE_FILE}" =~ \ |\' ]] ; then
      # https://github.com/koalaman/shellcheck/wiki/SC1090
      # shellcheck source=/dev/null
      source "${lMODULE_FILE}"
      (( lMODULE_COUNT+=1 ))
    fi
  done
  print_output "==> ""${GREEN}""Imported ""${lMODULE_COUNT}"" module/s""${NC}" "no_log"
}

sort_modules() {
  local lSORTED_MODULES_ARR=()
  local lMODULE_FILE=""
  for lMODULE_FILE in "${MODULES_ARR[@]}" ; do
    if ( file -b "${lMODULE_FILE}" | grep -q "shell script" ) && ! [[ "${lMODULE_FILE}" =~ \ |\' ]] ; then
      THREAD_PRIO=0
      # https://github.com/koalaman/shellcheck/wiki/SC1090
      # shellcheck source=/dev/null
      source "${lMODULE_FILE}"
      if [[ ${THREAD_PRIO} -eq 1 ]] ; then
        lSORTED_MODULES_ARR=( "${lMODULE_FILE}" "${lSORTED_MODULES_ARR[@]}" )
      else
        lSORTED_MODULES_ARR=( "${lSORTED_MODULES_ARR[@]}" "${lMODULE_FILE}" )
      fi
    fi
  done
  MODULES_ARR=( "${lSORTED_MODULES_ARR[@]}" )
}

# $1: module group letter [P, S, L, F, Q]
# $2: 0=single thread 1=multithread
# $3: HTML=1 - generate html file
run_modules() {
  MODULE_GROUP="${1:-}"
  printf -v THREADING_SET '%d\n' "${2}" 2>/dev/null
  THREADING_MOD_GROUP="${THREADING_SET}"

  local lSELECT_PRE_MODULES_COUNT=0

  for SELECT_NUM in "${SELECT_MODULES[@]}" ; do
    if [[ "${SELECT_NUM}" =~ ^["${MODULE_GROUP,,}","${MODULE_GROUP^^}"]{1} ]]; then
      (( lSELECT_PRE_MODULES_COUNT+=1 ))
    fi
  done

  if [[ "${#SELECT_MODULES[@]}" -eq 0 ]] || [[ "${lSELECT_PRE_MODULES_COUNT}" -eq 0 ]]; then
    export MODULES_ARR=()
    local lMODULES_LOCAL_ARR=()
    local lMODULES_EMBA_ARR=()
    local lMODULE_NAME=""

    mapfile -t lMODULES_EMBA_ARR < <(find "${MOD_DIR}" -maxdepth 1 -name "${MODULE_GROUP^^}""*_*.sh" | sort -V 2> /dev/null)
    if [[ -d "${MOD_DIR_LOCAL}" ]]; then
      mapfile -t lMODULES_LOCAL_ARR < <(find "${MOD_DIR_LOCAL}" -maxdepth 1 -name "${MODULE_GROUP^^}""*.sh" 2>/dev/null | sort -V 2> /dev/null)
    fi
    MODULES_ARR=( "${lMODULES_EMBA_ARR[@]}" "${lMODULES_LOCAL_ARR[@]}" )
    export MODULES_EXPORTED+=("${MODULES_ARR[@]}")
    if [[ ${THREADING_SET} -eq 1 && "${MODULE_GROUP^^}" != "P" ]] ; then
      sort_modules
    fi
    for lMODULE_FILE in "${MODULES_ARR[@]}" ; do
      # check if "${lMODULE_NAME}" is in blacklist from config directory and skip it
      lMODULE_NAME=$(basename -s .sh "${lMODULE_FILE}")
      if [[ " ${MODULE_BLACKLIST[*]} " =~  ${lMODULE_NAME}  ]]; then
        print_output "[*] $(print_date) - ${lMODULE_NAME} not executed - blacklist triggered " "main"
        continue
      fi
      if [[ "${SKIP_PRE_CHECKERS}" == 1 ]] && [[ "${MODULE_GROUP}" == "P" ]]; then
        print_output "[*] $(print_date) - ${lMODULE_NAME} not executed - skip pre-checkers is set " "main"
        continue
      fi
      local lMOD_FIN=0
      if ( file -b "${lMODULE_FILE}" | grep -q "shell script" ) && ! [[ "${lMODULE_FILE}" =~ \ |\' ]] ; then
        if [[ "${MODULE_GROUP^^}" == "P" ]] || [[ "${MODULE_GROUP^^}" == "D" ]]; then
          # we are able to enable/disable threading on module basis in the pre-checker modules with the header:
          # export PRE_THREAD_ENA=1/0
          # shellcheck source=/dev/null
          source "${lMODULE_FILE}"
          if [[ "${PRE_THREAD_ENA}" -eq 0 ]] ; then
            THREADING_SET=0
          fi
        fi

        local lMODULE_BN=""
        lMODULE_BN=$(basename "${lMODULE_FILE}")
        local lMODULE_MAIN=${lMODULE_BN%.*}
        # module_start_log "${lMODULE_MAIN}"
        if [[ "${RESTART}" -eq 1 ]]; then
          if [[ $(grep -i -c "${lMODULE_MAIN} finished" "${LOG_DIR}"/"${MAIN_LOG_FILE}") -gt 0 ]]; then
            if [[ "${lMODULE_MAIN}" == "P99_"* ]] || [[ "${lMODULE_MAIN}" == "L1"* ]]; then
              print_output "[*] $(print_date) - Module ${ORANGE}${lMODULE_MAIN}${NC} already finished but essential - rerun it" "main"
              lMOD_FIN=0
            else
              print_output "[*] $(print_date) - Module ${ORANGE}${lMODULE_MAIN}${NC} already finished ... skipping" "main"
              lMOD_FIN=1
              if [[ ${HTML} -eq 1 ]] ; then
                # we need to build the web reporter links for skipping modules
                local lLOG_FILES_ARR=()
                local lLOG_FILE_TMP=""
                mapfile -t lLOG_FILES_ARR < <(find "${LOG_DIR}" -maxdepth 1 -type f -iname "${lMODULE_MAIN}*.txt" | sort)
                for lLOG_FILE_TMP in "${lLOG_FILES_ARR[@]}"; do
                  if grep -q "nothing reported" "${lLOG_FILE_TMP}"; then
                    continue
                  fi
                  local lMODULE_NAME_TMP=""
                  lMODULE_NAME_TMP=$(basename -s .txt "${lLOG_FILE_TMP}")
                  local lMODUL_NAME=""
                  lMODUL_NAME="$(strip_color_tags "$(grep -a -E -B 1 '[=]{65}' "${lLOG_FILE_TMP}" | head -n 1 )" | cut -d" " -f2-)"
                  local lHTML_FILE="${lMODULE_NAME_TMP}.html"
                  add_link_to_index "${lHTML_FILE}" "${lMODUL_NAME}"
                done
              fi
            fi
          fi
        fi
        if [[ "${lMOD_FIN}" -eq 0 ]]; then
          if [[ ${THREADING_SET} -eq 1 ]]; then
            "${lMODULE_MAIN}" &
            local lMOD_PID="$!"
            store_kill_pids "${lMOD_PID}"
            WAIT_PIDS+=( "${lMOD_PID}" )
            max_pids_protection "${MAX_MODS}" WAIT_PIDS
          else
            "${lMODULE_MAIN}"
          fi
        fi
        reset_module_count
      fi
      if [[ "${MODULE_GROUP^^}" == "P" ]]; then
        THREADING_SET="${THREADING_MOD_GROUP}"
      fi
    done
  else
    # in full emulation mode we need to ensure that module s24 is always running
    if [[ "${MODULE_GROUP}" == "S" ]] && [[ "${FULL_EMULATION}" -eq 1 ]] && [[ ! "${SELECT_MODULES[*]}" =~ [sS]24 ]]; then
      print_output "[*] Automatically enable mandatory module S24 for system emulation mode"
      SELECT_MODULES+=("S24")
    fi

    if [[ "${MODULE_GROUP}" == "S" ]] && [[ "${SELECT_MODULES[*]}" =~ [sS]26 ]] && [[ ! "${SELECT_MODULES[*]}" =~ [sS]24 ]]; then
      print_output "[*] Automatically enable mandatory module S24 for S26 module"
      SELECT_MODULES+=("S24")
    fi

    # manual selection of pre-checking modules not supported. EMBA is doing this for you!
    if [[ "${SELECT_MODULES[*]}" =~ [pP][0-9] ]]; then
      print_ln "no_log"
      print_output "[!] ERROR: Manual pre-checking module selection not supported" "no_log"
      print_output "[!] Hint: Remove -m p[0-9]+ parameter" "no_log"
      print_ln "no_log"
      exit 1
    fi

    for SELECT_NUM in "${SELECT_MODULES[@]}" ; do
      local lMOD_FIN=0
      if [[ "${SELECT_NUM}" =~ ^["${MODULE_GROUP,,}","${MODULE_GROUP^^}"]{1}[0-9]+ ]]; then
        local MODULE=""
        MODULE=$(find "${MOD_DIR}" -maxdepth 1 -name "${MODULE_GROUP^^}""${SELECT_NUM:1}""_*.sh" | sort -V 2> /dev/null)
        # we need the whole module name including path in our array for later checks on it
        export MODULES_EXPORTED+=("${MODULE}")
        if ( file -b "${MODULE}" | grep -q "shell script" ) && ! [[ "${MODULE}" =~ \ |\' ]] ; then
          local lMODULE_BN=""
          lMODULE_BN=$(basename "${MODULE}")
          local lMODULE_MAIN=${lMODULE_BN%.*}
          # module_start_log "${lMODULE_MAIN}"
          if [[ "${RESTART}" -eq 1 ]]; then
            if [[ $(grep -i -c "${lMODULE_MAIN} finished" "${LOG_DIR}"/"${MAIN_LOG_FILE}") -gt 0 ]]; then
              if [[ "${lMODULE_MAIN}" == "P99_"* ]] || [[ "${lMODULE_MAIN}" == "L1"* ]]; then
                print_output "[*] $(print_date) - Module ${ORANGE}${lMODULE_MAIN}${NC} already finished but essential - rerun it" "main"
                lMOD_FIN=0
              else
                print_output "[*] $(print_date) - Module ${ORANGE}${lMODULE_MAIN}${NC} already finished ... skipping" "main"
                lMOD_FIN=1
                if [[ ${HTML} -eq 1 ]] ; then
                  # we need to build the web reporter links for skipping modules
                  local lLOG_FILES_ARR=()
                  local lLOG_FILE_TMP=""
                  mapfile -t lLOG_FILES_ARR < <(find "${LOG_DIR}" -maxdepth 1 -type f -iname "${lMODULE_MAIN}*.txt" | sort)
                  for lLOG_FILE_TMP in "${lLOG_FILES_ARR[@]}"; do
                    if grep -q "nothing reported" "${lLOG_FILE_TMP}"; then
                      continue
                    fi
                    local lMODULE_NAME_TMP=""
                    lMODULE_NAME_TMP=$(basename -s .txt "${lLOG_FILE_TMP}")
                    local lMODUL_NAME=""
                    lMODUL_NAME="$(strip_color_tags "$(grep -a -E -B 1 '[=]{65}' "${lLOG_FILE_TMP}" | head -n 1 )" | cut -d" " -f2-)"
                    local lHTML_FILE="${lMODULE_NAME_TMP}.html"
                    add_link_to_index "${lHTML_FILE}" "${lMODUL_NAME}"
                  done
                fi
              fi
            fi
          fi
          if [[ "${lMOD_FIN}" -eq 0 ]]; then
            if [[ ${THREADING_SET} -eq 1 ]]; then
              "${lMODULE_MAIN}" &
              local lMOD_PID="$!"
              store_kill_pids "${lMOD_PID}"
              WAIT_PIDS+=( "${lMOD_PID}" )
              max_pids_protection "${MAX_MODS}" WAIT_PIDS
            else
              "${lMODULE_MAIN}"
            fi
          fi
          reset_module_count
        fi
      elif [[ "${SELECT_NUM}" =~ ^["${MODULE_GROUP,,}","${MODULE_GROUP^^}"]{1} ]]; then
        export MODULES_ARR=()
        local lMODULES_LOCAL_ARR=()
        local lMODULES_EMBA_ARR=()
        mapfile -t lMODULES_EMBA_ARR < <(find "${MOD_DIR}" -maxdepth 1 -name "${MODULE_GROUP^^}""*_*.sh" | sort -V 2> /dev/null)
        if [[ -d "${MOD_DIR_LOCAL}" ]]; then
          mapfile -t lMODULES_LOCAL_ARR < <(find "${MOD_DIR_LOCAL}" -maxdepth 1 -name "${MODULE_GROUP^^}""*.sh" 2>/dev/null | sort -V 2> /dev/null)
        fi
        MODULES_ARR=( "${lMODULES_EMBA_ARR[@]}" "${lMODULES_LOCAL_ARR[@]}" )

        [[ ${THREADING_SET} -eq 1 ]] && sort_modules

        for lMODULE_FILE in "${MODULES_ARR[@]}" ; do
          # check if "${lMODULE_NAME}" is in blacklist from config directory and skip it
          lMODULE_NAME=$(basename -s .sh "${lMODULE_FILE}")
          if [[ " ${MODULE_BLACKLIST[*]} " =~  ${lMODULE_NAME}  ]]; then
            print_output "[*] $(print_date) - ${lMODULE_NAME} not executed - blacklist triggered " "main"
            continue
          fi
          local lMOD_FIN=0
          if ( file -b "${lMODULE_FILE}" | grep -q "shell script" ) && ! [[ "${lMODULE_FILE}" =~ \ |\' ]] ; then
            if [[ "${MODULE_GROUP^^}" == "P" ]] || [[ "${MODULE_GROUP^^}" == "D" ]]; then
              # we are able to enable/disable threading on module basis in the the pre-checker modules with the header:
              # export PRE_THREAD_ENA=1/0
              # shellcheck source=/dev/null
              source "${lMODULE_FILE}"
              if [[ "${PRE_THREAD_ENA}" -eq 0 ]] ; then
                THREADING_SET=0
              fi
            fi

            local lMODULE_BN=""
            lMODULE_BN=$(basename "${lMODULE_FILE}")
            local lMODULE_MAIN=${lMODULE_BN%.*}
            # module_start_log "${lMODULE_MAIN}"
            if [[ "${RESTART}" -eq 1 ]]; then
              if [[ $(grep -i -c "${lMODULE_MAIN} finished" "${LOG_DIR}"/"${MAIN_LOG_FILE}") -gt 0 ]]; then
                if [[ "${lMODULE_MAIN}" == "P99_"* ]] || [[ "${lMODULE_MAIN}" == "L1"* ]]; then
                  print_output "[*] $(print_date) - Module ${ORANGE}${lMODULE_MAIN}${NC} already finished but essential - rerun it" "main"
                  lMOD_FIN=0
                else
                  print_output "[*] $(print_date) - Module ${ORANGE}${lMODULE_MAIN}${NC} already finished ... skipping" "main"
                  lMOD_FIN=1
                  if [[ ${HTML} -eq 1 ]] ; then
                    # we need to build the web reporter links for skipping modules
                    local lLOG_FILES_ARR=()
                    local lLOG_FILE_TMP=""
                    mapfile -t lLOG_FILES_ARR < <(find "${LOG_DIR}" -maxdepth 1 -type f -iname "${lMODULE_MAIN}*.txt" | sort)
                    for lLOG_FILE_TMP in "${lLOG_FILES_ARR[@]}"; do
                      if grep -q "nothing reported" "${lLOG_FILE_TMP}"; then
                        continue
                      fi
                      local lMODULE_NAME_TMP=""
                      lMODULE_NAME_TMP=$(basename -s .txt "${lLOG_FILE_TMP}")
                      local lMODUL_NAME=""
                      lMODUL_NAME="$(strip_color_tags "$(grep -a -E -B 1 '[=]{65}' "${lLOG_FILE_TMP}" | head -n 1 )" | cut -d" " -f2-)"
                      local lHTML_FILE="${lMODULE_NAME_TMP}.html"
                      add_link_to_index "${lHTML_FILE}" "${lMODUL_NAME}"
                    done
                  fi
                fi
              fi
            fi
            if [[ "${lMOD_FIN}" -eq 0 ]]; then
              if [[ ${THREADING_SET} -eq 1 ]]; then
                "${lMODULE_MAIN}" &
                local lMOD_PID="$!"
                store_kill_pids "${lMOD_PID}"
                WAIT_PIDS+=( "${lMOD_PID}" )
                max_pids_protection "${MAX_MODS}" WAIT_PIDS
              else
                "${lMODULE_MAIN}"
              fi
            fi
            reset_module_count
          fi
          if [[ "${MODULE_GROUP^^}" == "P" ]]; then
            THREADING_SET="${THREADING_MOD_GROUP}"
          fi
        done
      fi
    done
  fi
}

main() {
  set -a
  trap cleaner INT

  INVOCATION_PATH="$(dirname "${0}")"

  export EMBA_PID="$$"

  # loads default values for EMBA
  export HELP_DIR="${INVOCATION_PATH}""/helpers"
  import_helper
  set_defaults

  print_ln "no_log"
  import_module

  welcome  # Print EMBA welcome message

  if [[ $# -eq 0 ]]; then
    print_output "\\n""${ORANGE}""In order to be able to use EMBA, you have to specify at least a firmware (-f)." "no_log"
    print_output "If you don't set a log directory (-l), then ./logs will be used.""${NC}" "no_log"
    print_help
    exit 1
  fi

  export EMBA_COMMAND
  EMBA_COMMAND="$(dirname "${0}")""/emba ""$*"

  emba_parameter_parsing "$@"
  set_log_paths

  # Validate RESCAN_SBOM mode requirements
  if [[ "${RESCAN_SBOM}" -eq 1 ]]; then
    if [[ -z "${LOG_DIR}" ]]; then
      print_output "[-] ${RED}ERROR:${NC} RESCAN_SBOM mode requires an existing log directory specified with ${ORANGE}-l${NC} parameter" "no_log"
      print_output "[-] The log directory must be from a previous EMBA scan and contain an SBOM" "no_log"
      exit 1
    fi
    if [[ ! -d "${LOG_DIR}" ]]; then
      print_output "[-] ${RED}ERROR:${NC} RESCAN_SBOM mode requires the log directory ${ORANGE}${LOG_DIR}${NC} to exist" "no_log"
      print_output "[-] Please specify a valid existing log directory from a previous EMBA scan" "no_log"
      exit 1
    fi
    # Finally check for the EMBA SBOM json log file required by F17
    if [[ ! -f "${EMBA_SBOM_JSON}" ]]; then
      print_output "[-] ${RED}ERROR:${NC} RESCAN_SBOM mode requires an existing EMBA SBOM file" "no_log"
      print_output "[-] Missing SBOM file: ${ORANGE}${EMBA_SBOM_JSON}${NC}" "no_log"
      print_output "[-] Please ensure the log directory is from a complete EMBA scan with F15 SBOM generation" "no_log"
      exit 1
    fi
  fi

  print_ln "no_log"

  write_notification "EMBA starting"

  # WSL support - currently experimental!
  if [[ ${IN_DOCKER} -eq 0 ]]; then
    if grep -q -i wsl /proc/version; then
      print_bar "no_log"
      print_output "[*] $(print_date) - INFO: System running in WSL environment!" "no_log"
      print_output "[*] $(print_date) - INFO: WSL is currently experimental." "no_log"
      print_output "[*] $(print_date) - INFO: Please report issues to https://github.com/e-m-b-a/emba/issues." "no_log"
      print_bar "no_log"
      export WSL=1
    fi
  fi

  # print it only once per EMBA run - not again from started container
  [[ ${IN_DOCKER} -eq 0 ]] && banner_printer

  if [[ ${IN_DOCKER} -eq 1 ]] ; then
    # set external path new for docker
    export EXT_DIR="/external"
  fi

  # activate the virtual environment - we should have it in external which fits also the docker environment
  # shellcheck source=/dev/null
  if [[ -f "${EXT_DIR}/emba_venv/bin/activate" ]]; then
    print_output "[*] $(print_date) - Enable python virtual environment ${ORANGE}${EXT_DIR}/emba_venv${NC}" "no_log"
    source "${EXT_DIR}/emba_venv/bin/activate"
  fi

  # EMBA should be started with "sudo -E" to access the proxy settings from the user
  export PROXY_SETTINGS=""
  PROXY_SETTINGS="$(env | grep "http_proxy=http" | cut -d = -f2 | sort -u | head -1)"
  if [[ -n "${PROXY_SETTINGS}" ]]; then
    # 2nd try with the original user:
    PROXY_SETTINGS="$(sudo -E -u "${SUDO_USER:-${USER}}" env | grep -E "http_proxy=http" | cut -d = -f2 | sort -u | head -1)"
  else
    if [[ -f "${BASIC_DATA_LOG_DIR}"/orig_user.log ]]; then
      if grep -q "PROXY: http" "${BASIC_DATA_LOG_DIR}"/orig_user.log; then
        PROXY_SETTINGS="$(grep "PROXY: http" "${BASIC_DATA_LOG_DIR}"/orig_user.log | awk '{print $2}')"
      fi
    fi
  fi

  # Check all dependencies of EMBA
  dependency_check

  if [[ "${ONLY_DEP}" -eq 0 ]]; then
    if [[ "${UPDATE}" -eq 1 ]]; then
      write_notification "EMBA starts with update"
      emba_updater
      exit 0
    fi

    if [[ ${USE_DOCKER} -eq 0 && ${IN_DOCKER} -eq 0 ]]; then
      print_bar "no_log"
      print_output "[!] $(print_date) - WARNING: EMBA running in developer mode!" "no_log"
      write_notification "WARNING: EMBA running in developer mode"
      print_bar "no_log"
    fi

    enable_strict_mode "${STRICT_MODE}" 1

    # profile handling
    if [[ -n "${PROFILE:-}" ]]; then
      if [[ -f "${PROFILE}" ]]; then
        print_bar "no_log"
        if [[ ${IN_DOCKER} -ne 1 ]] ; then
          print_output "[*] $(print_date) - Loading EMBA scan profile with the following settings:" "no_log"
        else
          print_output "[*] $(print_date) - Loading EMBA scan profile." "no_log"
        fi
        # all profile output and settings are done by the profile file located in ./scan-profiles/
        # shellcheck source=/dev/null
        source "${PROFILE}"
        print_output "[*] $(print_date) - Profile ${PROFILE} loaded." "no_log"
        print_bar "no_log"
      else
        print_output "[!] $(print_date) - Profile ${PROFILE} not found." "no_log"
        exit 1
      fi
    fi

    if [[ "${ONLY_DEP}" -eq 0 ]]; then
      # check provided paths for validity
      check_path_valid "${FIRMWARE_PATH}"
      check_path_valid "${KERNEL_CONFIG}"
      check_path_valid "${LOG_DIR}"
    fi

    # restart file gets generated during startup if old log dir is found:
    if [[ ${IN_DOCKER} -eq 1 && "${CONTAINER_NUMBER}" -eq 1 ]] || [[ "${USE_DOCKER}" -eq 0 && ${IN_DOCKER} -eq 0 ]]; then
      if [[ -f "${TMP_DIR}"/restart_emba ]]; then
        print_output "[!] $(print_date) - Found restart file and backup_vars file ... trying to restart EMBA scan" "no_log"
        export RESTART=1
        rm "${TMP_DIR}"/restart_emba
        # shellcheck source=/dev/null
        source "${LOG_DIR}""/backup_vars.log"
      fi
    fi

    # check if LOG_DIR exists and prompt to terminal to delete its content (Y/n)
    [[ ${IN_DOCKER} -eq 0 ]] && log_folder

    # create log directory, if not exists and needed subdirectories
    # do not create a log dir for dep check
    export MAIN_LOG="${LOG_DIR}""/""${MAIN_LOG_FILE}"
    export ERROR_LOG="${LOG_DIR}"/emba_error.log
    [[ "${ONLY_DEP}" -eq 0 ]] && create_log_dir

    # kernel downloader runs on the host and waits for an identified kernel version. Afterwards
    # it tries to download the kernel sources for further analysis
    if [[ ${IN_DOCKER} -eq 0 ]] && [[ "${RESCAN_SBOM}" -eq 0 ]]; then
      kernel_downloader &
      K_DOWN_PID="$!"
      store_kill_pids "${K_DOWN_PID}"
      print_output "[*] $(print_date) - Started kernel downloader thread with PID ${ORANGE}${K_DOWN_PID}${NC}" "no_log"
    fi

    if [[ ${IN_DOCKER} -eq 0 ]]; then
      echo "${LOG_DIR}" > "${TMP_DIR}"/orig_logdir
    fi

    if [[ "${IN_DOCKER}" -eq 0 ]]; then
      print_notification &
      NOTIFICATION_PID="$!"
      store_kill_pids "${NOTIFICATION_PID}"
      disown "${NOTIFICATION_PID}" 2> /dev/null || true
      print_output "[*] $(print_date) - Original user: ${ORANGE}${SUDO_USER:-${USER}}${NC}" "no_log"
      print_output "[*] $(print_date) - Notification process started with PID ${ORANGE}${NOTIFICATION_PID}${NC}" "no_log"
      PROXY="$(sudo -E -u "${SUDO_USER:-${USER}}" env | grep -E "http(s)_proxy" | cut -d = -f2 || true)"
      echo "${SUDO_USER:-${USER}}" > "${BASIC_DATA_LOG_DIR}"/orig_user.log
      {
        echo "UID: $(id -u "${SUDO_USER:-${USER}}")"
        echo "GID: $(id -g "${SUDO_USER:-${USER}}")"
        echo "PROXY: ${PROXY}"
      } >> "${BASIC_DATA_LOG_DIR}"/orig_user.log
    fi

    if [[ "${IN_DOCKER}" -eq 0 ]]; then
      print_running_modules &
      RUN_MOD_PID="$!"
      store_kill_pids "${RUN_MOD_PID}"
      echo "${RUN_MOD_PID}" > "${LOG_DIR}"/print_running_modules.pid
      disown "${RUN_MOD_PID}" 2> /dev/null || true
    fi

    # Print additional information about the firmware (-Y, -X, -Z, -N)
    print_firmware_info "${FW_VENDOR}" "${FW_VERSION}" "${FW_DEVICE}" "${FW_NOTES}"
    if [[ "${KERNEL}" -ne 1 ]] && [[ "${CONTAINER_EXTRACT}" -ne 1 ]] && [[ "${ONLY_DEP}" -eq 0 ]] && [[ "${RESCAN_SBOM}" -eq 0 ]]; then
      check_init_size
    fi

    # Now we have the firmware and log path, lets set some additional paths
    FIRMWARE_PATH="$(abs_path "${FIRMWARE_PATH}")"

    # Check firmware type (file/directory)
    # copy the firmware outside of the docker and not a second time within the docker
    if [[ -d "${FIRMWARE_PATH}" ]] ; then
      PRE_CHECK=1
      print_output "[*] $(print_date) - Firmware directory detected." "no_log"
      print_output "[*] $(print_date) - EMBA starts with testing the environment." "no_log"
      if [[ ${IN_DOCKER} -eq 0 ]] ; then
        # in docker environment the firmware is already available
        print_output "    The provided firmware will be copied to ${ORANGE}""${FIRMWARE_PATH_CP}""/""$(basename "${FIRMWARE_PATH}")""${NC}" "no_log"
        cp -pRPf "${FIRMWARE_PATH}" "${FIRMWARE_PATH_CP}""/""$(basename "${FIRMWARE_PATH}")"
        FIRMWARE_PATH="${FIRMWARE_PATH_CP}""/""$(basename "${FIRMWARE_PATH}")"
        export OUTPUT_DIR="${FIRMWARE_PATH_CP}"
      else
        # need to set it as fallback:
        export OUTPUT_DIR="${FIRMWARE_PATH}"
      fi
    elif [[ "${CONTAINER_EXTRACT}" -eq 1 ]]; then
      PRE_CHECK=1
      print_output "[*] $(print_date) - Firmware analysis of docker image starting." "no_log"
      print_output "    EMBA starts with extracting the docker image ${ORANGE}${CONTAINER_ID}${NC}." "no_log"
      export FIRMWARE_PATH="${LOG_DIR}"/firmware/firmware_docker_extracted.tar
      export OUTPUT_DIR="${FIRMWARE_PATH}"
      export FIRMWARE=1
    elif [[ -f "${FIRMWARE_PATH}" ]] && [[ -z "${FIRMWARE_PATH1}" ]]; then
      PRE_CHECK=1
      if [[ -n "${KERNEL_CONFIG}" && "${KERNEL}" -eq 1 ]]; then
        print_output "[*] $(print_date) - Kernel configuration file detected." "no_log"
      else
        print_output "[*] $(print_date) - Firmware binary detected." "no_log"
      fi
      print_output "    EMBA starts with the pre-testing phase." "no_log"
      export OUTPUT_DIR="${FIRMWARE_PATH}"
    elif [[ -f "${FIRMWARE_PATH}" ]] && [[ -f "${FIRMWARE_PATH1}" ]]; then
      DIFF_MODE=1
      print_output "[*] $(print_date) - Multiple firmware binaries detected." "no_log"
      print_output "    EMBA starts in firmware diff mode." "no_log"
    elif [[ "${RESCAN_SBOM}" -eq 1 ]]; then
      print_output "[*] $(print_date) - Rescan SBOM mode detected." "no_log"
      print_output "    EMBA will reuse existing SBOM for fresh CVE analysis." "no_log"
      export OUTPUT_DIR="${LOG_DIR}"
    else
      print_output "[!] $(print_date) - Invalid firmware file" "no_log"
      print_help
      exit 1
    fi

    # calculate the maximum modules are running in parallel
    if [[ ${THREADED} -eq 1 ]] && [[ "${MAX_MODS}" -eq 0 ]]; then
      # the maximum modules in parallel
      # rule of thumb - per core half a module, minimum 2 modules
      MAX_MODS="$(( "$(nproc)" /2 +1))"

      # if we have only one core we run two modules in parallel
      if [[ "${MAX_MODS}" -lt 2 ]]; then
        MAX_MODS=2
      fi
      export MAX_MODS
    fi

    # calculate the maximum threads per module
    if [[ ${THREADED} -eq 1 ]] && [[ "${MAX_MOD_THREADS}" -eq 0 ]]; then
      # the maximum threads per modules - if this value does not match adjust it via
      # local MAX_MOD_THREADS=123 in module area
      export MAX_MOD_THREADS="$(( 2 * "$(nproc)" ))"
    fi

    # setup non threaded mode:
    if [[ ${THREADED} -eq 0 ]]; then
      export MAX_MODS=1
      export MAX_MOD_THREADS=1
    fi
    if [[ "${MAX_MODS}" -eq 1 ]] || [[ "${MAX_MOD_THREADS}" -eq 1 ]]; then
      print_ln "no_log"
      print_output "[!] WARNING: EMBA is running with ${ORANGE}${MAX_MODS}${MAGENTA} modules in parallel and ${ORANGE}${MAX_MOD_THREADS}${MAGENTA} threads per module." "no_log"
      print_output "[!] This scan could take a very long time ..." "no_log"
    else
      print_output "$(indent "EMBA is running with ${ORANGE}${MAX_MODS}${NC} modules in parallel and ${ORANGE}${MAX_MOD_THREADS}${NC} threads per module.")" "no_log"
    fi

    # Change log output to color for web report and prepare report
    if [[ ${HTML} -eq 1 ]] ; then
      if [[ ${FORMAT_LOG} -eq 0 ]] ; then
        FORMAT_LOG=1
        print_output "[*] $(print_date) - Activate colored log for webreport" "no_log"
      fi
      print_output "[*] $(print_date) - Prepare webreport" "no_log"
      prepare_report
    fi

    if [[ "${KERNEL}" -ne 1 ]] && [[ ${FIRMWARE} -eq 1 ]]; then
      # Exclude paths from testing and set EXCL_FIND for find command (prune paths dynamically)
      set_exclude
    fi

    #######################################################################################
    # Kernel configuration check
    #######################################################################################
    if [[ "${KERNEL}" -eq 1 ]]; then
      if [[ ${IN_DOCKER} -eq 1 ]] && [[ -f "${LOG_DIR}"/kernel_config ]]; then
        export KERNEL_CONFIG="${LOG_DIR}"/kernel_config
      fi

      if ! [[ -f "${KERNEL_CONFIG}" ]] ; then
        print_output "[-] $(print_date) - Invalid kernel configuration file: ${ORANGE}${KERNEL_CONFIG}${NC}" "no_log"
        exit 1
      else
        if [[ ${IN_DOCKER} -eq 0 ]] ; then
          # we copy the kernel config file from outside the container into our log directory
          # further modules are using LOG_DIR/kernel_config for accessing the kernel config
          if [[ -d "${LOG_DIR}" ]] ; then
            cp "${KERNEL_CONFIG}" "${LOG_DIR}"/kernel_config
          else
            print_output "[!] $(print_date) - Missing log directory" "no_log"
            exit 1
          fi
        fi
      fi
    fi

    # disk space monitor not fully working -> removed for now
    # Todo: check it and fix it
    # disk_space_monitor "${EMBA_PID}" &
    # local TMP_PID="$!"
    # store_kill_pids "${TMP_PID}"
    # disown "${TMP_PID}" 2> /dev/null || true

    # if $CONTAINER_EXTRACT is set we extract the docker container with id $CONTAINER_ID outside of the
    # EMBA container into log directory
    # we do this outside of the EMBA container - otherwise we will not reach the docker environment
    if [[ "${CONTAINER_EXTRACT}" -eq 1 && "${IN_DOCKER}" -eq 0 ]] ; then
      docker_container_extractor "${CONTAINER_ID}"
    fi
  fi

  #######################################################################################
  # Docker
  #######################################################################################
  if [[ ${USE_DOCKER} -eq 1 ]] ; then
    if ! [[ ${EUID} -eq 0 ]] ; then
      if ! groups | grep -qw docker; then
        print_output "[!] $(print_date) - Using EMBA with docker-compose requires root permissions" "no_log"
        print_output "$(indent "Run EMBA with root permissions or add your user to docker group")" "no_log"
        print_output "$(indent "e.g., sudo usermod -aG docker [non-root user]")" "no_log"
        exit 1
      fi
    fi

    OPTIND=1
    ARGUMENTS=()
    while getopts a:A:BcC:d:De:Ef:Fhijk:l:m:N:o:p:P:qQRrsStT:UX:yY:WzZ: OPT ; do
      case ${OPT} in
        D|f|i|l|o)
          ;;
        *)
          if [[ -v OPTARG[@] ]] ; then
            ARGUMENTS=( "${ARGUMENTS[@]}" "-${OPT}" "${OPTARG[@]}" )
          else
            ARGUMENTS=( "${ARGUMENTS[@]}" "-${OPT}" )
          fi
          ;;
      esac
    done

    if [[ "${IN_DOCKER}" -eq 0 ]] && [[ "${SILENT}" -eq 1 ]]; then
      initial_status_bar &
      # Trap the window resize signal (handle window resize events).
      trap 'initial_status_bar' WINCH
    fi

    print_output "[*] $(print_date) - EMBA sets up the docker environment.\\n" "no_log"

    # try it 10 times before exit
    local lCNT_DOCK=0
    for lCNT_DOCK in {1..10}; do
      if ! docker images 2>/dev/null | grep -qE "embeddedanalyzer/emba"; then
        sleep 1
      else
        break
      fi
      if [[ "${lCNT_DOCK}" -ge 10 ]]; then
        print_output "[-] $(print_date) - EMBA docker environment not ready!" "no_log"
        exit 1
      fi
    done

    print_output "[*] $(print_date) - EMBA initializes docker container.\\n" "no_log"


    if [[ "${ONLY_DEP}" -eq 0 ]]; then
      # store some details that we do not have in the docker container:
      echo "${FIRMWARE_PATH}" >> "${BASIC_DATA_LOG_DIR}"/fw_name.log
      if [[ "${DIFF_MODE}" -gt 0 ]]; then
        echo "${FIRMWARE_PATH1}" >> "${BASIC_DATA_LOG_DIR}"/fw_name.log
      fi
      echo "${LOG_DIR}" >> "${BASIC_DATA_LOG_DIR}"/emba_log_dir.log
      echo "${EMBA_COMMAND}" >> "${BASIC_DATA_LOG_DIR}"/emba_command.log
    fi

    write_notification "EMBA starting docker container"

    [[ "${STRICT_MODE}" -eq 1 ]] && set +e

    disable_strict_mode "${STRICT_MODE}" 0
    if [[ "${ONLY_DEP}" -gt 0 ]]; then
      # check dependencies mode:
      COMPOSE_IGNORE_ORPHANS=True EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" LOG="/tmp" "${DOCKER_COMPOSE[@]}" run --rm emba -c './emba -l /logs -f /firmware -i "$@"' _ "${ARGUMENTS[@]}"
      D_RETURN=$?
      COMPOSE_IGNORE_ORPHANS=True EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" LOG="/tmp" "${DOCKER_COMPOSE[@]}" run --rm emba_quest -c './emba -l /logs -f /firmware -i "$@"' _ "${ARGUMENTS[@]}"
      if [[ $? -ne ${D_RETURN} ]]; then
        D_RETURN=1
      fi
    elif [[ "${DIFF_MODE}" -gt 0 ]]; then
      FIRMWARE_PATH1="$(abs_path "${FIRMWARE_PATH1}")"
      # firmware diff mode - needs two firmware files:
      COMPOSE_IGNORE_ORPHANS=True EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" LOG="${LOG_DIR}" "${DOCKER_COMPOSE[@]}" run -v "${FIRMWARE_PATH1}":/firmware2 --rm emba -c './emba -l /logs -f /firmware -o /firmware2 -i "$@"' _ "${ARGUMENTS[@]}"
      D_RETURN=$?
    else
      print_ln "no_log"
      welcome
      # default EMBA analysis mode:
      if [[ "${RESCAN_SBOM}" -ne 1 ]]; then
        local lQUEST_CONTAINER_=""
        lQUEST_CONTAINER_="$(COMPOSE_IGNORE_ORPHANS=True EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" LOG="${LOG_DIR}" "${DOCKER_COMPOSE[@]}" run --detach --rm emba_quest -c './emba -l /logs -f /firmware -i "$@"' _ "${ARGUMENTS[@]}")"
        export QUEST_CONTAINER="${lQUEST_CONTAINER_}"
        print_output "[+] $(print_date) - Quest container ${lQUEST_CONTAINER_} started and detached.\\n" "no_log"
      fi

      # EMBA startup:
      if [[ "${SILENT}" -eq 0 ]]; then
        # EMBA container starts in the foreground and we can see all the logs
        COMPOSE_IGNORE_ORPHANS=True EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" LOG="${LOG_DIR}" "${DOCKER_COMPOSE[@]}" run --rm emba -c './emba -l /logs -f /firmware -i "$@"' _ "${ARGUMENTS[@]}"
        D_RETURN=$?
      else
        # EMBA container starts in the background
        print_output "[*] $(print_date) - EMBA main container starting and detaching.\\n" "no_log"
        local lMAIN_CONTAINER_=""
        lMAIN_CONTAINER_="$(COMPOSE_IGNORE_ORPHANS=True EMBA="${INVOCATION_PATH}" FIRMWARE="${FIRMWARE_PATH}" LOG="${LOG_DIR}" "${DOCKER_COMPOSE[@]}" run --detach --rm emba -c './emba -l /logs -f /firmware -i "$@"' _ "${ARGUMENTS[@]}")"
        export MAIN_CONTAINER="${lMAIN_CONTAINER_}"

        print_output "[+] $(print_date) - EMBA main container ${lMAIN_CONTAINER_} started and detached." "no_log"
        print_ln "no_log"
        # the following write_log is used as the status bar shows the main emba.log and with a print_output we get double output
        write_log "[*] $(print_date) - EMBA main container ${lMAIN_CONTAINER_} monitoring through ${ORANGE}docker logs ${lMAIN_CONTAINER_} -f${NC}." "${LOG_DIR}/emba.log"
        if [[ "$( docker container inspect -f '{{.State.Status}}' "${lQUEST_CONTAINER_}" )" == "running" ]]; then
          write_log "[*] $(print_date) - EMBA quest container ${lQUEST_CONTAINER_} monitoring through ${ORANGE}docker logs ${lQUEST_CONTAINER_} -f${NC}." "${LOG_DIR}/emba.log"
        fi

        while [[ "$( docker container inspect -f '{{.State.Status}}' "${lMAIN_CONTAINER_}" 2>/dev/null)" == "running" ]]; do
          sleep 1
        done
        remove_status_bar
        # reset
        printf "\x1Bc"

        if [[ -f "${LOG_DIR}"/f50_base_aggregator.txt ]]; then
          cat "${LOG_DIR}"/f50_base_aggregator.txt
        fi
        D_RETURN=$(docker inspect "${lMAIN_CONTAINER_}" --format='{{.State.ExitCode}}' 2>/dev/null)
      fi
    fi
    enable_strict_mode "${STRICT_MODE}" 0

    if [[ "${D_RETURN}" -eq 0 ]] ; then
      if [[ "${ONLY_DEP}" -eq 0 ]] ; then
        print_output "[*] $(print_date) - EMBA finished analysis in default mode (docker container).\\n" "no_log"
        write_notification "EMBA finished analysis in default mode"
        print_output "[*] $(print_date) - Firmware tested: ${ORANGE}${FIRMWARE_PATH}${NC}" "no_log"
        print_output "[*] $(print_date) - Log directory: ${ORANGE}${LOG_DIR}${NC}" "no_log"
        if [[ -v HTML_PATH ]] && [[ -f "${HTML_PATH}"/index.html ]]; then
          print_output "[*] $(print_date) - Access the web-report with ${ORANGE}firefox $(abs_path "${HTML_PATH}/index.html")${NC}" "main"
        fi
        cleaner 0
        print_output "[!] $(print_date) - Test ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "main"
      else
        # we do not need the log dir from dependency checker
        [[ -d "${LOG_DIR}" ]] && rm -rf "${LOG_DIR}"
        print_output "[!] $(print_date) - Test ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "no_log"
      fi
      info_printer "no_log"
      exit 0
    else
      print_output "[-] $(print_date) - Test ended on ""$(print_date)"" and EMBA failed in docker mode!" "main"
      cleaner 0
      write_notification "EMBA failed analysis in default mode"
      exit 1
    fi
  fi

  #######################################################################################
  # RESCAN_SBOM mode - Skip to F17 module execution only
  #######################################################################################
  if [[ "${RESCAN_SBOM}" -eq 1 ]]; then
    print_output "[*] $(print_date) - Rescan SBOM mode enabled" "main"
    print_output "[*] $(print_date) - Skipping all phases except reporting (F17 CVE analysis)" "main"
    export TESTING_DONE=1
  fi

  #######################################################################################
  # Quests (Q-modules)
  #######################################################################################

  if [[ -v CONTAINER_NUMBER ]] && [[ "${DIFF_MODE}" -ne 1 ]] && [[ "${RESCAN_SBOM}" -eq 0 ]]; then
    if [[ "${CONTAINER_NUMBER}" -eq 2 ]] ;  then
      while ! grep -q "Pre-checking phase started" "${LOG_DIR}"/"${MAIN_LOG_FILE}"; do
        sleep 1
      done
      run_modules "Q" "${THREADED}" "0"
      [[ "${THREADED}" -eq 1 ]] && wait_for_pid "${WAIT_PIDS[@]}"
      print_output "[*] $(print_date) - Quest container finished" "main"
      exit 0
    elif [[ "${USE_DOCKER}" -eq 0 && "${IN_DOCKER}" -eq 0 ]] ;  then
      # currently we do not support the Quest containers in dev mode
      while ! grep -q "Pre-checking phase started" "${LOG_DIR}"/"${MAIN_LOG_FILE}"; do
        sleep 1
      done
      # dev-mode
      run_modules "Q" "${THREADED}" "0" &
      export Q_MOD_PID="$!"
    elif [[ "${CONTAINER_NUMBER}" -ne 1 ]]; then
      docker logs "${QUEST_CONTAINER}" --follow | grep "ok" && print_output "[*] Quest container ${QUEST_CONTAINER} failed the dependency check" "main"
    fi
  fi

  #######################################################################################
  # Pre-Check (P-modules)
  #######################################################################################
  if [[ "${PRE_CHECK}" -eq 1 ]] && [[ "${DIFF_MODE}" -ne 1 ]] && [[ "${RESCAN_SBOM}" -eq 0 ]]; then

    print_ln "no_log"
    if [[ -d "${LOG_DIR}" ]]; then
      print_output "[!] $(print_date) - Pre-checking phase started on ""$(print_date)""\\n""$(indent "${NC}""Firmware binary path: ""${FIRMWARE_PATH}")" "main"
    else
      print_output "[!] $(print_date) - Pre-checking phase started on ""$(print_date)""\\n""$(indent "${NC}""Firmware binary path: ""${FIRMWARE_PATH}")" "no_log"
    fi
    write_notification "Pre-checking phase started"

    # 'main' functions of imported modules
    # in the pre-check phase we execute all modules with P[Number]_Name.sh

    run_modules "P" "${THREADED}" "0"

    # if we running threaded we are going to wait for the slow guys here
    [[ ${THREADED} -eq 1 ]] && wait_for_pid "${WAIT_PIDS[@]}"

    print_ln "no_log"

    if [[ -d "${LOG_DIR}" ]]; then
      print_output "[!] $(print_date) - Pre-checking phase ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "main"
    else
      print_output "[!] $(print_date) - Pre-checking phase ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "no_log"
    fi
    write_notification "Pre-checking phase finished"

    # useful prints for debugging:
    # print_output "[!] Firmware value: ${FIRMWARE}"
    # print_output "[!] Firmware path: ${FIRMWARE}_PATH"
    # print_output "[!] Output dir: $OUTPUT_DIR"
    # print_output "[!] LINUX_PATH_COUNTER: $LINUX_PATH_COUNTER"
    # print_output "[!] LINUX_PATH_ARRAY: ${#ROOT_PATH[@]}"
  fi

  #######################################################################################
  # Diff mode (D-modules)
  #######################################################################################
  if [[ "${DIFF_MODE}" -eq 1 ]] && [[ "${RESCAN_SBOM}" -eq 0 ]]; then

    print_ln "no_log"
    if [[ -d "${LOG_DIR}" ]]; then
      print_output "[!] $(print_date) - Diff mode started on ""$(print_date)" "main"
      print_output "$(indent "${NC}""1st Firmware binary path: ""${FIRMWARE_PATH}")" "main"
      print_output "$(indent "${NC}""2nd Firmware binary path: ""${FIRMWARE_PATH1}")" "main"
    else
      print_output "[!] $(print_date) - Diff mode started on ""$(print_date)"
      print_output "$(indent "${NC}""1st Firmware binary path: ""${FIRMWARE_PATH}")" "no_log"
      print_output "$(indent "${NC}""2nd Firmware binary path: ""${FIRMWARE_PATH1}")" "no_log"
    fi
    write_notification "Diff mode started"

    run_modules "D" "${THREADED}" "0"

    if [[ -d "${LOG_DIR}" ]]; then
      print_output "[!] $(print_date) - Diff mode ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "main"
    else
      print_output "[!] $(print_date) - Diff mode ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "no_log"
    fi
    write_notification "Diff mode finished"

    TESTING_DONE=1

  fi

  #######################################################################################
  # Firmware-Check (S modules)
  #######################################################################################
  WAIT_PIDS=()
  if [[ "${FIRMWARE}" -eq 1 ]] && [[ "${DIFF_MODE}" -ne 1 ]] && [[ "${RESCAN_SBOM}" -eq 0 ]]; then
    print_output "\n=================================================================\n" "no_log"

    if [[ -d "${LOG_DIR}" ]]; then
      print_output "[!] $(print_date) - Testing phase started on ""$(print_date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "main"
    else
      print_output "[!] $(print_date) - Testing phase started on ""$(print_date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "no_log"
    fi
    write_notification "Testing phase started"

    run_modules "S" "${THREADED}" "${HTML}"

    [[ ${THREADED} -eq 1 ]] && wait_for_pid "${WAIT_PIDS[@]}"

    print_ln "no_log"

    if [[ -d "${LOG_DIR}" ]]; then
      print_output "[!] $(print_date) - Testing phase ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "main"
    else
      print_output "[!] $(print_date) - Testing phase ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "no_log"
    fi
    write_notification "Testing phase ended"

    TESTING_DONE=1
  fi

  #######################################################################################
  # Live Emulation - Check (L-modules)
  #######################################################################################
  if [[ "${FULL_EMULATION}" -eq 1 ]] && [[ "${DIFF_MODE}" -ne 1 ]] && [[ "${RESCAN_SBOM}" -eq 0 ]]; then
    print_output "\n=================================================================\n" "no_log"
    if [[ -d "${LOG_DIR}" ]]; then
      print_output "[!] $(print_date) - System emulation phase started on ""$(print_date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "main"
    else
      print_output "[!] $(print_date) - System emulation phase started on ""$(print_date)""\\n""$(indent "${NC}""Firmware path: ""${FIRMWARE_PATH}")" "no_log"
    fi
    write_notification "System emulation phase started"

    # these modules are not threaded!
    run_modules "L" "0" "${HTML}"

    print_ln "no_log"
    if [[ -d "${LOG_DIR}" ]]; then
      print_output "[!] $(print_date) - System emulation phase ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "main"
    else
      print_output "[!] $(print_date) - System emulation ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "no_log"
    fi
    write_notification "System emulation phase ended"
  fi

  #######################################################################################
  # Reporting (F-modules)
  #######################################################################################
  if [[ -d "${LOG_DIR}" ]]; then
    print_output "[!] $(print_date) - Reporting phase started on ""$(print_date)""\\n" "main"
  else
    print_output "[!] $(print_date) - Reporting phase started on ""$(print_date)""\\n" "no_log"
  fi
  write_notification "Reporting phase started"

  run_modules "F" "0" "${HTML}"

  [[ ${DISABLE_STATUS_BAR} -eq 0 ]] && remove_status_bar

  write_notification "Reporting phase ended"

  if [[ "${USE_DOCKER}" -eq 0 && "${THREADED}" -eq 1 && "${IN_DOCKER}" -eq 0 ]] && [[ -n "${Q_MOD_PID}" ]];  then
    wait_for_pid "${Q_MOD_PID}"
  fi

  if [[ "${TESTING_DONE}" -eq 1 ]]; then
    if [[ "${FINAL_FW_RM}" -eq 1 && -d "${LOG_DIR}"/firmware ]]; then
      print_output "[*] $(print_date) - Removing temp firmware directory\\n" "no_log"
      rm -r "${LOG_DIR}"/firmware 2>/dev/null
    fi
    if [[ "${FINAL_FW_RM}" -eq 1 && -d "${LOG_DIR}"/p61_unblob_eval/unblob_extracted ]]; then
      print_output "[*] $(print_date) - Removing unblob firmware directory\\n" "no_log"
      rm -r "${LOG_DIR}"/p61_unblob_eval/unblob_extracted 2>/dev/null
    fi
    print_ln "no_log"
    if [[ -d "${LOG_DIR}" ]]; then
      print_output "[!] $(print_date) - Test ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "main"
      write_notification "EMBA finished analysis"
      rm -r "${TMP_DIR}" 2>/dev/null || true
    else
      print_output "[!] $(print_date) - Test ended on ""$(print_date)"" and took about ""$(show_runtime)"" \\n" "no_log"
    fi
  else
    print_output "[!] $(print_date) - No extracted firmware found" "no_log"
    print_output "$(indent "Try using binwalk or something else to extract the firmware")"
    exit 1
  fi

  [[ "${HTML}" -eq 1 ]] && update_index

  if [[ -f "${HTML_PATH}"/index.html ]] && [[ "${IN_DOCKER}" -eq 0 ]]; then
    print_output "[*] $(print_date) - Web report created HTML report in ${ORANGE}${LOG_DIR}/html-report${NC}\\n" "main"
    print_output "[*] $(print_date) - Open the web-report with${ORANGE} firefox $(abs_path "${HTML_PATH}/index.html")${NC}\\n" "main"
  fi

  # deactivate the python virtual env
  [[ -v VIRTUAL_ENV ]] && deactivate

  # we need to change the permissions of the LOG_DIR to the orig. user from the host
  [[ "${IN_DOCKER}" -eq 1 ]] && restore_permissions
  cleaner 0
  info_printer "no_log"

  exit 0
}

main "$@"
